{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "assert sys.version_info >= (3, 5)\n",
    "import numpy as np\n",
    "%matplotlib inline\n",
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "mpl.rc('axes', labelsize=14)\n",
    "mpl.rc('xtick', labelsize=12)\n",
    "mpl.rc('ytick', labelsize=12)\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "import pandas as pd\n",
    "from scipy import special\n",
    "from tensorflow.keras import layers\n",
    "np.random.seed(42)\n",
    "tf.random.set_seed(42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "M = 16\n",
    "k = int(np.log2(M))\n",
    "n = 1\n",
    "TRAINING_SNR = 7\n",
    "rayleigh = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def EbNo_to_noise(ebnodb):\n",
    "    '''Transform EbNo[dB]/snr to noise power'''\n",
    "    ebno = 10**(ebnodb/10)\n",
    "    noise_std = 1/np.sqrt(2*(k/n)*ebno) \n",
    "    return noise_std\n",
    "\n",
    "def SNR_to_noise(snrdb):\n",
    "    '''Transform EbNo[dB]/snr to noise power'''\n",
    "    snr = 10**(snrdb/10)\n",
    "    noise_std = 1/np.sqrt(2*snr)\n",
    "    return noise_std"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# From https://colab.research.google.com/github/google-research/google-research/blob/master/vbmi/vbmi_demo.ipynb#scrollTo=LwSIGeXlD11E\n",
    "# concatenated critic\n",
    "\n",
    "class NN_function(tf.keras.Model):\n",
    "    def __init__(self, hidden_dim, layers, activation, **extra_kwargs):\n",
    "        super(NN_function, self).__init__()\n",
    "        self._f = tf.keras.Sequential(\n",
    "          [tf.keras.layers.Dense(hidden_dim, activation) for _ in range(layers)] +\n",
    "          [tf.keras.layers.Dense(1)])\n",
    "\n",
    "    def call(self, x, y):\n",
    "        batch_size = tf.shape(x)[0]\n",
    "        x_tiled = tf.tile(x[None, :],  (batch_size, 1, 1))\n",
    "        y_tiled = tf.tile(y[:, None],  (1, batch_size, 1))\n",
    "        xy_pairs = tf.reshape(tf.concat((x_tiled, y_tiled), axis=2), [batch_size * batch_size, -1])\n",
    "        scores = self._f(xy_pairs)\n",
    "        return tf.transpose(tf.reshape(scores, [batch_size, batch_size]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "critic_params = {\n",
    "    'layers': 2,\n",
    "    'hidden_dim': 256,\n",
    "    'activation': 'relu',\n",
    "}\n",
    "\n",
    "def MINE(scores):  \n",
    "    def marg(x):\n",
    "        batch_size = x.shape[0]\n",
    "        marg_ = tf.reduce_mean(tf.exp(x - tf.linalg.tensor_diag(np.inf * tf.ones(batch_size))))\n",
    "        return marg_*((batch_size*batch_size)/(batch_size*(batch_size-1.)))\n",
    "    joint_term = tf.reduce_mean(tf.linalg.diag_part(scores))\n",
    "    marg_term = marg(scores)\n",
    "    return joint_term - tf.math.log(marg_term)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "noise_std = EbNo_to_noise(TRAINING_SNR)\n",
    "# custom functions / layers without weights\n",
    "norm_layer = keras.layers.Lambda(lambda x: tf.divide(x,tf.sqrt(2*tf.reduce_mean(tf.square(x)))))\n",
    "shape_layer = keras.layers.Lambda(lambda x: tf.reshape(x, shape=[-1,2,n]))\n",
    "shape_layer2 = keras.layers.Lambda(lambda x: tf.reshape(x, shape=[-1,2*n]))\n",
    "channel_layer = keras.layers.Lambda(lambda x: \n",
    "                    x + tf.random.normal(tf.shape(x), mean=0.0, stddev=noise_std))\n",
    "def sample_Rayleigh_channel(x, noise_std):\n",
    "    h_sample = (1/np.sqrt(2))*tf.sqrt(tf.random.normal(tf.shape(x))**2+tf.random.normal(tf.shape(x))**2)\n",
    "    z_sample = tf.random.normal(tf.shape(x), stddev = noise_std)\n",
    "    y_sample = x + tf.divide(z_sample,h_sample)\n",
    "    return tf.cast(y_sample, tf.float32)\n",
    "\n",
    "rayleigh_channel_layer = keras.layers.Lambda(lambda x: sample_Rayleigh_channel(x,noise_std))\n",
    "\n",
    "\n",
    "encoder = keras.models.Sequential([\n",
    "            keras.layers.Embedding(M, M, embeddings_initializer='glorot_normal', input_length=1),\n",
    "            keras.layers.Dense(M, activation=\"elu\"),\n",
    "            keras.layers.Dense(2*n, activation=None),\n",
    "            shape_layer,\n",
    "            norm_layer])\n",
    "if rayleigh:\n",
    "    channel = keras.models.Sequential([rayleigh_channel_layer])\n",
    "else:\n",
    "    channel = keras.models.Sequential([channel_layer])\n",
    "\n",
    "decoder = keras.models.Sequential([\n",
    "                keras.layers.InputLayer(input_shape=[2,n]),\n",
    "                shape_layer2,\n",
    "                keras.layers.Dense(M, activation=\"elu\"),\n",
    "                keras.layers.Dense(M, activation=\"softmax\")\n",
    "                ])\n",
    "\n",
    "autoencoder = keras.models.Sequential([encoder, channel, decoder])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def B_Ber_m(input_msg, msg):\n",
    "    '''Calculate the Batch Bit Error Rate'''\n",
    "    batch_size = input_msg.shape[0]\n",
    "    pred_error = tf.not_equal(tf.reshape(input_msg, shape=(-1,batch_size)), tf.argmax(msg, 1)) \n",
    "    bber = tf.reduce_mean(tf.cast(pred_error, tf.float32))\n",
    "    return bber"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def random_sample(batch_size=32):\n",
    "    msg = np.random.randint(M, size=(batch_size,1))\n",
    "    return msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_encoding(M=16, n=1):\n",
    "    inp = np.arange(0,M)\n",
    "    coding = encoder.predict(inp)\n",
    "    fig = plt.figure(figsize=(4,4))\n",
    "    plt.plot(coding[:,0], coding[:, 1], \"b.\")\n",
    "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
    "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
    "    plt.grid(True)\n",
    "    plt.gca().set_ylim(-2, 2)\n",
    "    plt.gca().set_xlim(-2, 2)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_noisy_codeword(data):\n",
    "    rcvd_word = data[1:2000]\n",
    "    fig = plt.figure(figsize=(4,4))\n",
    "    plt.plot(rcvd_word[:,0], rcvd_word[:, 1], \"b.\")\n",
    "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
    "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
    "    plt.grid(True)\n",
    "    plt.gca().set_ylim(-2, 2)\n",
    "    plt.gca().set_xlim(-2, 2)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_fn = keras.losses.SparseCategoricalCrossentropy()\n",
    "mean_loss = keras.metrics.Mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_loss(step, epoch, mean_loss, X_batch, y_pred, plot_encoding):\n",
    "    template = 'Iteration: {}, Epoch: {}, Loss: {:.5f}, Batch_BER: {:.5f}'\n",
    "    if step % 10 == 0:\n",
    "        print(template.format(step, epoch, mean_loss.result(), B_Ber_m(X_batch, y_pred)))\n",
    "        if plot_encoding:\n",
    "            test_encoding()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_batch_loss(epoch, mean_loss, X_batch, y_pred):\n",
    "        template_outer_loop = 'Interim result for Epoch: {}, Loss: {:.5f}, Batch_BER: {:.5f}'\n",
    "        print(template_outer_loop.format(epoch, mean_loss.result(), B_Ber_m(X_batch, y_pred)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_mi(NN_estimation, n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.005):\n",
    "    optimizer_mi = keras.optimizers.Nadam(lr=learning_rate)\n",
    "    for epoch in range(1, n_epochs + 1): \n",
    "        print(\"Training in Epoch {}/{}\".format(epoch, n_epochs)) \n",
    "        for step in range(1, n_steps + 1):\n",
    "            X_batch = random_sample(batch_size)\n",
    "            with tf.GradientTape() as tape:\n",
    "                x_enc = encoder(X_batch, training=True)\n",
    "                y_recv = channel(x_enc)\n",
    "                x = tf.reshape(x_enc, shape=[batch_size,2*n])\n",
    "                y = tf.reshape(y_recv, shape=[batch_size,2*n])\n",
    "                score = NN_estimation(x,y)\n",
    "                loss = -MINE(score)\n",
    "                gradients = tape.gradient(loss, NN_estimation.trainable_variables) \n",
    "                optimizer_mi.apply_gradients(zip(gradients, NN_estimation.trainable_variables))\n",
    "            mi_avg = -mean_loss(loss)\n",
    "        print('Epoch: {}, Mi is {}'.format(epoch, mi_avg))\n",
    "        mean_loss.reset_states()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_decoder(n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.005, plot_encoding=True):\n",
    "    optimizer_ae = keras.optimizers.Nadam(lr=learning_rate)\n",
    "    for epoch in range(1, n_epochs + 1): \n",
    "        print(\"Training Bob in Epoch {}/{}\".format(epoch, n_epochs)) \n",
    "        for step in range(1, n_steps + 1):\n",
    "            X_batch  = random_sample(batch_size)\n",
    "            with tf.GradientTape() as tape:\n",
    "                y_pred = autoencoder(X_batch, training=True)\n",
    "                loss = tf.reduce_mean(loss_fn(X_batch, y_pred))\n",
    "                gradients = tape.gradient(loss, decoder.trainable_variables) \n",
    "                optimizer_ae.apply_gradients(zip(gradients, decoder.trainable_variables)) \n",
    "            mean_loss(loss)\n",
    "            plot_loss(step, epoch, mean_loss, X_batch, y_pred, plot_encoding)\n",
    "        plot_batch_loss(epoch, mean_loss, X_batch, y_pred) \n",
    "        mean_loss.reset_states()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_encoder(NN_estimation, n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.05):\n",
    "    optimizer_mi = keras.optimizers.Nadam(lr=0.005)\n",
    "    optimizer_ae = keras.optimizers.Nadam(lr=learning_rate)\n",
    "    for epoch in range(1, n_epochs + 1): \n",
    "        print(\"Training Bob in Epoch {}/{}\".format(epoch, n_epochs)) \n",
    "        for step in range(1, n_steps + 1):\n",
    "            X_batch  = random_sample(batch_size)\n",
    "            with tf.GradientTape() as tape:\n",
    "                x_enc = encoder(X_batch, training=True)\n",
    "                y_recv = tf.grad_pass_through(channel)(x_enc) #forward pass:channel; backward pass Identity\n",
    "                x = tf.reshape(x_enc, shape=[batch_size,2*n])\n",
    "                y = tf.reshape(y_recv, shape=[batch_size,2*n])\n",
    "                score = NN_estimation(x,y)\n",
    "                loss = -MINE(score)\n",
    "                gradients = tape.gradient(loss, encoder.trainable_variables) \n",
    "                optimizer_ae.apply_gradients(zip(gradients, encoder.trainable_variables))\n",
    "            mi_avg = -mean_loss(loss)\n",
    "        with tf.GradientTape() as tape:\n",
    "            X_batch  = random_sample(batch_size) \n",
    "            x_enc = encoder(X_batch, training=True)\n",
    "            y_recv = channel(x_enc)\n",
    "            x = tf.reshape(x_enc, shape=[batch_size,2*n])\n",
    "            y = tf.reshape(y_recv, shape=[batch_size,2*n])\n",
    "            score = NN_estimation(x,y)\n",
    "            loss = -MINE(score)\n",
    "            gradients = tape.gradient(loss, NN_estimation.trainable_variables) \n",
    "            optimizer_mi.apply_gradients(zip(gradients, NN_estimation.trainable_variables))\n",
    "        print('Epoch: {}, Mi is {}'.format(epoch, mi_avg))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def Test_AE():\n",
    "    '''Calculate Bit Error for varying SNRs'''\n",
    "    snr_range = np.linspace(0, 15, 31)\n",
    "    bber_vec = [None] * len(snr_range)\n",
    "        \n",
    "    for db in range(len(snr_range)):\n",
    "        for it in range(1,1000):\n",
    "            noise_std = EbNo_to_noise(snr_range[db])\n",
    "            X_batch  = random_sample(500)\n",
    "            code_word = encoder(X_batch)\n",
    "            if rayleigh:\n",
    "                rcvd_word = sample_Rayleigh_channel(code_word, noise_std)\n",
    "            else:\n",
    "                rcvd_word = code_word + tf.random.normal(tf.shape(code_word), mean=0.0, stddev=noise_std)\n",
    "            dcoded_msg = decoder(rcvd_word)\n",
    "            bber = B_Ber_m(X_batch, dcoded_msg)\n",
    "            bber_avg = mean_loss(bber)\n",
    "        bber_vec[db] = bber_avg\n",
    "        mean_loss.reset_states()\n",
    "        if (db % 6 == 0) & (db > 0):\n",
    "            print(f'Progress: {db} of {30} parts')\n",
    "\n",
    "    return (snr_range, bber_vec)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training in Epoch 1/1\n",
      "Epoch: 1, Mi is 1.492371678352356\n",
      "Training Bob in Epoch 1/5\n",
      "Epoch: 1, Mi is 1.5568511486053467\n",
      "Training Bob in Epoch 2/5\n",
      "Epoch: 2, Mi is 1.602770209312439\n",
      "Training Bob in Epoch 3/5\n",
      "Epoch: 3, Mi is 1.652502179145813\n",
      "Training Bob in Epoch 4/5\n",
      "Epoch: 4, Mi is 1.6629245281219482\n",
      "Training Bob in Epoch 5/5\n",
      "Epoch: 5, Mi is 1.6791517734527588\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAASMAAAEWCAYAAAAtl/EzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAUeklEQVR4nO3df6zddX3H8eeLe7VFSg2yrvsDC2kiFcpWFSa5w4VqF5uakZpoDAoLjbi6OdThcIOlTS+0sZNEnL/G1llSIEzbhCJTnHMx3inaZEFd2epsnUqR8WMDtfY2tcX2vT++52yH03PvPef2fL+fz/d7X4/khHPu+dzb94eb87rfn5+3IgIzs9TOSF2AmRk4jMwsEw4jM8uCw8jMsuAwMrMsOIzMLAsOIzPLQvIwkjRP0nZJByUdlvQdSWumGX+jpKclHZJ0l6R5VdZrZuVIHkbAKPBj4ErgpcBGYJekC7oHSloN3AysAi4AlgK3VlSnmZVIOV6BLelR4NaIuL/r638HPBYRf956vQq4LyJ+LUGZZjZEo6kL6CZpMXAhsK/H28uBBzte7wUWSzo3Ip7r+jnrgfUA8+fPv3TJkiUlVZzeyZMnOeOMHDZyy9Hk+TV5bgAHDhx4NiIW9TM2qzCS9CLgPuDuiPhejyELgEMdr9vPzwZeEEYRsQ3YBrBs2bLYv3//8AvOxMTEBCtXrkxdRmmaPL8mzw1A0sF+x2YTyZLOAO4FjgM3TDFsEljY8br9/HCJpZlZBbIII0kCtgOLgbdExPNTDN0HrOh4vQJ4pnsXzczqJ4swAu4ELgKuioij04y7B7he0sWSzgE2ADsqqM/MSpY8jCSdD7wbeBXwtKTJ1uMaSUtaz5cARMSXgNuBrwIHW49NqWo3s+FJfgA7Ig4CmmbIgq7xdwB3lFqUmVUu+ZaRmRk4jMwsEw4jM8uCw8jMsuAwMrMsOIzMLAsOIzPLgsPIzLLgMDKzLDiMzCwLDiMzy4LDyMyy4DAysyw4jMwsCw4jM8uCw8jMsuAwMrMsJA8jSTdIekTSMUk7phm3TtKJjmVpJyWtrK5SMytT8mVngSeBLcBq4MwZxu6JiNeVX5KZVS15GEXEbgBJlwHnJS7HzBJJvps2oFdLelbSAUkbJSUPUzMbjjp9mL8GXELRnmg5sBP4JbC112BJ64H1AIsWLWJiYqKaKhOYnJz0/GqqyXMblCIidQ0ASNoCnBcR6/ocfzXwwYi4dKaxy5Yti/37959mhflqer/2Js+vyXMDkPStiLisn7F1203rFEzfb83MaiR5GEkalTQfGAFGJM3vdSxI0hpJi1vPXwlsBB6stlozK0vyMAI2AEeBm4FrW883dLe2BlYBj0o6AnwR2A18KEXBZjZ8yQ9gR8Q4MD7F2ws6xt0E3FRBSWaWQA5bRmZmDiMzy4PDyMyy4DAysyw4jMwsCw4jM8uCw8jMsuAwMrMsOIzMLAsOIzPLgsPIzLLgMDKzLDiMzCwLDiMzy4LDyMyy4DCyWtqzB7ZuLf5rzZB8cTWzQe3ZA6tWwfHj8OIXw1e+AmNjqauy0+UtI6udiYkiiE6cKP7rTj/NkDyMJN0g6RFJxyTtmGHsjZKelnRI0l2S5lVUpmVk5cpii2hkpPhvgzv9zCnJwwh4EtgC3DXdIEmrKRbtXwVcACwFbi27OMvP2Fixa7Z5s3fRmiT5MaOI2A0g6TLgvGmGXgdsj4h9rfGbgfsoAsrmmLExh1DTJA+jASznhX3S9gKLJZ0bEc91D3Z76+Zo8vyaPLdB1SmMFgCHOl63n58NnBJGEbEN2AZFe+smtxCeTYvkPXuKA78rV+a/hdHkFtBNntug6hRGk8DCjtft54cT1FJrPjVuOcrhAHa/9gErOl6vAJ7ptYtm0/OpcctR8jCSNCppPjACjEiaL6nXFts9wPWSLpZ0DkVb7B0VltoYPjVuOUoeRhShcpTirNi1recbJC2RNClpCUBEfAm4HfgqcLD12JSm5HrzqXHLUfJjRhExDoxP8faCrrF3AHeUXNKc4FPjlpsctozMzBxGZpYHh5GZZcFhZGZZcBiZWRYcRmaWBYeRmWXBYWRmWXAYmVkWHEZmlgWHkVnJ3FapP8nvTTNrMq8d1T9vGZmVyGtH9c9hZFYirx3VP++mmZWovXZUXdYbT8lhZFYyrx3VH++mmVkWsggjSS+T9ICkI5IOSnrHFOPGJT3fWo62/Vhadb1mNny57KZ9CjgOLAZeBTwkaW+7e2yXnRFxbaXVmVnpkm8ZSToLeAuwMSImI+Jh4O+B30tbmZlVKYctowuBExFxoONre4Erpxh/laSfAE8Bn4yIO3sNcnvr5mjy/Jo8t0HlEEbdbatpvT67x9hdFC2rnwEuB+6X9LOI+Ez3QLe3TmuY7bNznN+wNHlug8ohjLrbVtN6fUrb6oj4bsfLb0r6GPBW4JQwsnR8C4TNRvJjRsABYFTSKzq+toKinfVMAlApVdms+RYIm43kYRQRR4DdwG2SzpJ0BbAWuLd7rKS1ks5R4bXA+4AHq63YZuJbIGw2cthNA3gPcBfw38BzwB9GxD5Jvw38Q0S0O8te3Ro3D3gC+HBE3J2iYJuab4Gw2cgijCLiJ8Cbe3z963S0uI6It1dZl82eb4GwQSXfTTMzA4dR9rxKoM0VWeymWW8+RW5zibeMMuZT5DaXOIwy5lPk5fHub368m5YxnyIvh3d/89TXlpGkMyU9IelxSfO63vu0pBOSri6nxLltbAxuucUflmHy7m+e+gqjiDgKbAJeTnGBIgCStgLXA++NiM+WUqHZkHn3N0+DHDPaQXG/2C2SFkj6Y+BmYFNE/FUZxZmVob37u3mzd9Fy0vcxo4g4Ielm4PPA54A3AJ+IiNvKKs6sLL5CPD8DnU2LiC8A3wZWATuB93e+L2mepL+V9MPW+tTfb21BmZlNa6CzaZLeRrFGNcDhiIgeP+9p4I3AD4HfAP5R0lMRsfN0izWz5up7y0jSGymW9XgA+CzwTkkXdY6JiCMRsTEi/jMiTkbEvwIPAVcMs2gza55+T+1fTrHm0DeAa4ANwElg6wzfNwq8Dnj09Mo0s6abMYxaWz8PUazI+OaIOBYRPwC2A2tbi6FN5eMU61nfM4xizay5pg0jSUuAL1MEypqI+HnH27cBR4Hbp/jej1BsFa2JiOPDKdfMmmraA9gR8TjFhY693nsKeEmv9yT9JcUZtzdExLOnW6SZNd/Qb5SV9HHgdyiC6H/6/J5+21tL0oclPdd63C7JC/KbNcBQb5SVdD7wXuAY8KOOnPh6RKyZ5lv7bW+9nmJ52hUUnUH+ieISgr8e2iTMLImhhlFEHGTA1kEd7a0viYhJ4GFJ7fbWN3cNvw74SEQ80frejwC/j8PIrPZyWEJkkPbWy1vvdY5b3uuHur11czR5fk2e26ByCKNB2lt3jz0ELJCk7qvB3d66OZo8vybPbVA5rPTYd3vrHmMXApM9bksxs5rJIYwGaW+9r/XeTOPMGmEuLY+bfDctIo5Iare3fhfF2bS1wG/1GH4P8AFJX6Q4m/YnwCcqK9YqtWdPsQrjwoUL5+QCaHNtedzkYdTSb3vrvwGWAv/Wev3p1tfsNLU/+Lmstd35QRwdXcFrXpNHXVXqtTxuk/8fZBFGA7S3DuBPWw8bkhz/And+ECPU+A9iL+3lcdu/l6ZvHWYRRpZWjn+BOz+Io6PR+A9iL3OtO4zDyLL8C9z5QVy4cC9jY69JXVISc2l5XIeRZfsXuP1BnJj4+cyDrfYcRgbMrb/AlqccrjMyM3MYmVkeHEZmlgWHkZllwWFkZllwGJlZFhxGZpYFh5GZZcFhZGZZcBiZWRYcRmaWBYeRnZamLIvalHnUmW+UtVnLcVG22WjKPOou+ZZRv62tW2PHJT0vabLjsbTKeu3/9VqUrY6aMo+6Sx5GvLC19TXAnZJ6NmZs2RkRCzoeP6ykSjtFe1G2kZF8FmWbjabMo+6S7qYN2NraMpPromyDaso86k4p+x9KejXwzYg4s+NrNwFXRsRVPcaPAzcCJ4CngE9GxJ1T/OzO9taX7tq1a/gTyMTk5CQLFiyYeWBNNXl+TZ4bwOtf//pvRcRl/YxNfQB7kNbWALsoWlY/A1wO3C/pZxHxme6Bbm/dHE2eX5PnNqhSjxlJmpAUUzweZrDW1kTEdyPiyYg4ERHfBD4GvLXMOZhZNUrdMoqIldO93zpmNCrpFRHx/daXB2lZHYBmX6GZ5SLp2bSIOAK0W1ufJekKitbW9/YaL2mtpHNUeC3wPuDB6ipOzxfnWVOlPmYEU7S2BujR3vrq1th5wBPAhyPi7upLTsMX51mTJQ+jqVpbt97rbm/99qrqylGOnV/NhiWHix6tT744z5os+ZaR9c8X51mTOYxqxp1fram8m2ZmWXAYmVkWHEZmJfO1Yf3xMSOzEvnasP55y8isRF64rX8OI6uluuz6+Nqw/nk3zWqnTrs+vjasfw4jq5263Rbja8P64900qx3v+jSTt4ysdrzr00wOI6sl7/o0j3fTzCwLDiMzy4LDyMyykDSMJN0g6RFJxyTt6GP8jZKelnRI0l2S5lVQpplVIPWW0ZPAFop1raclaTVFl9lVwAXAUuDWMoszs+qk7g6yOyI+R7EQ/0yuA7ZHxL6I+CmwGVhXZn1mVp06ndpfzgvbEu0FFks6NyJOCbOu9tZMNPgOxcnJSc+vppo8t0HVKYy6W2G3n59Njy0rt7dujibPr8lzG1Rpu2l9tLYeVHcr7Pbznq2wzaxeStsymqm19Szso2h9vav1egXwTK9dNDOrn9Sn9kclzQdGgBFJ8yVNFZD3ANdLuljSOcAGYEdFpZpZyVKf2t8AHKU4ZX9t6/kGAElLJE1KWgIQEV8Cbge+ChxsPTalKNrMhi/pAeyIGAfGp3jvcTpaW7e+dgdwR+mFmVnlUm8ZmZkBDiMzy4TDyMyy4DAysyw4jMwsCw4jM8uCw8j6UpemiVZfdbpR1hKpU9NEqy9vGdmM3C/equAwshm5aaJVwbtpNiM3TbQqOIysL26aaGXzbprZDHwmsRreMjKbhs8kVsdbRmbT8JnE6jiMzKbhM4nV8W6a2TR8JrE6qdfA7ru9taR1kk60lqJtP1ZWU6nNZWNjcMstDqKypd4yare3Xg2c2cf4PRHxunJLMrMUUq+BvRtA0mXAeSlrMbO06nYA+9WSnpV0QNLGadoamVnN1OnD/DXgEooWRcuBncAvga29BktaD6wHWLRoUaP7mTe9X3uT59fkuQ1KEVHOD5YmgCunePsbncd+JG0BzouIdQP8/KuBD0bEpTONXbZsWezfv7/fH107Te/X3uT5NXluAJK+FRGX9TO2Tu2tT/knAJX8b5hZRVKf2u+7vbWkNZIWt56/EtgIPFhdtWZWptQHsPtubw2sAh6VdAT4IrAb+FD1JZtZGVKf2h+nz/bWEXETcFMlhZlZ5VJvGZmZAQ4jM8uEw8jMsuAwMrMsOIzMLAsOIzPLgsPIzLLgMDKzLDiMzCwLDiMzy4LDyMyy4DAysyw4jMwsCw4jM8uCw8jMsuAwMrMsOIzMLAsOIzPLQrIwkjRP0nZJByUdlvQdSWtm+J4bJT0t6ZCkuyTNq6peMytXyi2jUeDHFL3VXkrR7WOXpAt6DZa0mmLh/lXABcBS4NYK6jSzCiQLo4g4EhHjEfFYRJyMiC8APwKmasp4HbA9IvZFxE+BzcC6iso1s5Jl09661RPtQmDfFEOW88I+aXuBxZLOjYjnevy8/2tvDRyT9O/DrDczvwI8m7qIEjV5fk2eG8CyfgdmEUaSXgTcB9wdEd+bYtgC4FDH6/bzs4FTwigitgHbWj//kX5b7NaR51dfTZ4bFPPrd2xpu2mSJiTFFI+HO8adAdwLHAdumOZHTgILO163nx8eevFmVrnStowiYuVMYyQJ2A4sBt4UEc9PM3wfsALY1Xq9Anim1y6amdVP6uuM7gQuAq6KiKMzjL0HuF7SxZLOoWiDvaPPf2fb7EusBc+vvpo8NxhgfoqIMguZ+h+WzgceA44Bv+x4690RcZ+kJcB3gYtbra6R9AHgz4AzgfuBP4iIY5UWbmalSBZGZmadUu+mmZkBDiMzy8ScCKPZ3AdXN5JukPSIpGOSdqSuZxgkvUzSA5KOtH5370hd07A08ffVNtvPWxYXPVag8z64x4E3UdwH9+sR8VjKwoboSWALsJriAH8TfIri+rPFwKuAhyTtjYiprtKvkyb+vtpm9XmbswewJT0K3BoR96euZZgkbQHOi4h1qWs5HZLOAn4KXBIRB1pfuxf4r4i4OWlxQ9SU39dM+vm8zYndtG593Adn6V0InGgHUcteinsUrUb6/bzNuTDq8z44S6/7XkRar89OUIvN0iCft0aEUQn3wWWl3/k1TPe9iLRe+17Emhj089aIA9gl3AeXlX7m10AHgFFJr4iI77e+tgLvWtfCbD5vjdgy6tMg98HVjqRRSfOBEWBE0nxJtf1jExFHgN3AbZLOknQFsJbiL23tNe331cPgn7eIaPwDOB8I4BcUm//txzWpaxviHMdbc+x8jKeu6zTn9DLgc8ARilPE70hdk39ffc1tVp+3OXtq38zyMpd208wsYw4jM8uCw8jMsuAwMrMsOIzMLAsOIzPLgsPIzLLgMDKzLDiMzCwLDiNLTtKZkp6Q9LikeV3vfVrSCUlXp6rPquEwsuSiuJFyE/By4D3tr0vaClwPvDciPpuoPKuI702zLEgaoVjJ8VeBpcC7gI8CmyLitpS1WTUcRpYNSb8LfB74CvAG4JMR8b60VVlVvJtm2YiILwDfBlYBO4H3d4+R9EeS/kXSLyRNVFyilahJizlZzUl6G0VLIoDD0Xuz/SngL4DfBMaqqs3K5zCyLEh6I8Uqjg8AzwPvlPTRiPiPznERsbs1fkn1VVqZvJtmyUm6nGKJ2W8A1wAbgJPA1pR1WbUcRpaUpIuAhygW4H9zRByLiB9QLOa+trX2tc0BDiNLprWr9WWKfmhrIuLnHW/fBhwFbk9Rm1XPx4wsmYh4nOJCx17vPQW8pNqKLCWHkdVKq51P+3FGq93PyYg4nrYyO10OI6ubDRS3jrQdBf4ZWJmkGhsaX4FtZlnwAWwzy4LDyMyy4DAysyw4jMwsCw4jM8uCw8jMsuAwMrMs/C/Y3JS8udmq4gAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training Bob in Epoch 1/5\n",
      "Iteration: 10, Epoch: 1, Loss: -1.65714, Batch_BER: 0.87600\n",
      "Iteration: 20, Epoch: 1, Loss: -1.63611, Batch_BER: 0.74200\n",
      "Iteration: 30, Epoch: 1, Loss: -1.61605, Batch_BER: 0.71600\n",
      "Iteration: 40, Epoch: 1, Loss: -1.59700, Batch_BER: 0.69600\n",
      "Iteration: 50, Epoch: 1, Loss: -1.57886, Batch_BER: 0.58200\n",
      "Iteration: 60, Epoch: 1, Loss: -1.56161, Batch_BER: 0.57400\n",
      "Iteration: 70, Epoch: 1, Loss: -1.54512, Batch_BER: 0.50600\n",
      "Iteration: 80, Epoch: 1, Loss: -1.52916, Batch_BER: 0.51400\n",
      "Iteration: 90, Epoch: 1, Loss: -1.51376, Batch_BER: 0.46200\n",
      "Iteration: 100, Epoch: 1, Loss: -1.49888, Batch_BER: 0.44800\n",
      "Iteration: 110, Epoch: 1, Loss: -1.48439, Batch_BER: 0.44000\n",
      "Iteration: 120, Epoch: 1, Loss: -1.47028, Batch_BER: 0.35200\n",
      "Iteration: 130, Epoch: 1, Loss: -1.45654, Batch_BER: 0.35400\n",
      "Iteration: 140, Epoch: 1, Loss: -1.44305, Batch_BER: 0.35000\n",
      "Iteration: 150, Epoch: 1, Loss: -1.42987, Batch_BER: 0.29400\n",
      "Iteration: 160, Epoch: 1, Loss: -1.41705, Batch_BER: 0.27800\n",
      "Iteration: 170, Epoch: 1, Loss: -1.40444, Batch_BER: 0.27400\n",
      "Iteration: 180, Epoch: 1, Loss: -1.39206, Batch_BER: 0.29800\n",
      "Iteration: 190, Epoch: 1, Loss: -1.37971, Batch_BER: 0.29800\n",
      "Iteration: 200, Epoch: 1, Loss: -1.36757, Batch_BER: 0.26200\n",
      "Iteration: 210, Epoch: 1, Loss: -1.35559, Batch_BER: 0.28200\n",
      "Iteration: 220, Epoch: 1, Loss: -1.34381, Batch_BER: 0.24800\n",
      "Iteration: 230, Epoch: 1, Loss: -1.33236, Batch_BER: 0.21800\n",
      "Iteration: 240, Epoch: 1, Loss: -1.32118, Batch_BER: 0.27200\n",
      "Iteration: 250, Epoch: 1, Loss: -1.30975, Batch_BER: 0.29800\n",
      "Iteration: 260, Epoch: 1, Loss: -1.29853, Batch_BER: 0.26800\n",
      "Iteration: 270, Epoch: 1, Loss: -1.28755, Batch_BER: 0.25600\n",
      "Iteration: 280, Epoch: 1, Loss: -1.27659, Batch_BER: 0.23800\n",
      "Iteration: 290, Epoch: 1, Loss: -1.26584, Batch_BER: 0.27400\n",
      "Iteration: 300, Epoch: 1, Loss: -1.25523, Batch_BER: 0.27600\n",
      "Iteration: 310, Epoch: 1, Loss: -1.24465, Batch_BER: 0.25800\n",
      "Iteration: 320, Epoch: 1, Loss: -1.23403, Batch_BER: 0.26600\n",
      "Iteration: 330, Epoch: 1, Loss: -1.22379, Batch_BER: 0.26200\n",
      "Iteration: 340, Epoch: 1, Loss: -1.21348, Batch_BER: 0.26800\n",
      "Iteration: 350, Epoch: 1, Loss: -1.20323, Batch_BER: 0.26400\n",
      "Iteration: 360, Epoch: 1, Loss: -1.19319, Batch_BER: 0.26600\n",
      "Iteration: 370, Epoch: 1, Loss: -1.18337, Batch_BER: 0.25400\n",
      "Iteration: 380, Epoch: 1, Loss: -1.17355, Batch_BER: 0.24600\n",
      "Iteration: 390, Epoch: 1, Loss: -1.16378, Batch_BER: 0.28200\n",
      "Iteration: 400, Epoch: 1, Loss: -1.15395, Batch_BER: 0.24000\n",
      "Interim result for Epoch: 1, Loss: -1.15395, Batch_BER: 0.24000\n",
      "Training Bob in Epoch 2/5\n",
      "Iteration: 10, Epoch: 2, Loss: 1.14930, Batch_BER: 0.25800\n",
      "Iteration: 20, Epoch: 2, Loss: 1.13347, Batch_BER: 0.24000\n",
      "Iteration: 30, Epoch: 2, Loss: 1.14584, Batch_BER: 0.24200\n",
      "Iteration: 40, Epoch: 2, Loss: 1.16014, Batch_BER: 0.31000\n",
      "Iteration: 50, Epoch: 2, Loss: 1.15145, Batch_BER: 0.22400\n",
      "Iteration: 60, Epoch: 2, Loss: 1.14673, Batch_BER: 0.24200\n",
      "Iteration: 70, Epoch: 2, Loss: 1.14519, Batch_BER: 0.23000\n",
      "Iteration: 80, Epoch: 2, Loss: 1.14594, Batch_BER: 0.22400\n",
      "Iteration: 90, Epoch: 2, Loss: 1.14592, Batch_BER: 0.26400\n",
      "Iteration: 100, Epoch: 2, Loss: 1.14458, Batch_BER: 0.26200\n",
      "Iteration: 110, Epoch: 2, Loss: 1.14764, Batch_BER: 0.23600\n",
      "Iteration: 120, Epoch: 2, Loss: 1.14567, Batch_BER: 0.23600\n",
      "Iteration: 130, Epoch: 2, Loss: 1.14654, Batch_BER: 0.27200\n",
      "Iteration: 140, Epoch: 2, Loss: 1.14540, Batch_BER: 0.25600\n",
      "Iteration: 150, Epoch: 2, Loss: 1.14472, Batch_BER: 0.26000\n",
      "Iteration: 160, Epoch: 2, Loss: 1.14426, Batch_BER: 0.28600\n",
      "Iteration: 170, Epoch: 2, Loss: 1.14565, Batch_BER: 0.26600\n",
      "Iteration: 180, Epoch: 2, Loss: 1.14516, Batch_BER: 0.24600\n",
      "Iteration: 190, Epoch: 2, Loss: 1.14362, Batch_BER: 0.27000\n",
      "Iteration: 200, Epoch: 2, Loss: 1.14267, Batch_BER: 0.27600\n",
      "Iteration: 210, Epoch: 2, Loss: 1.14348, Batch_BER: 0.22000\n",
      "Iteration: 220, Epoch: 2, Loss: 1.14468, Batch_BER: 0.23400\n",
      "Iteration: 230, Epoch: 2, Loss: 1.14473, Batch_BER: 0.29000\n",
      "Iteration: 240, Epoch: 2, Loss: 1.14474, Batch_BER: 0.26600\n",
      "Iteration: 250, Epoch: 2, Loss: 1.14363, Batch_BER: 0.24200\n",
      "Iteration: 260, Epoch: 2, Loss: 1.14356, Batch_BER: 0.26600\n",
      "Iteration: 270, Epoch: 2, Loss: 1.14465, Batch_BER: 0.25400\n",
      "Iteration: 280, Epoch: 2, Loss: 1.14540, Batch_BER: 0.26400\n",
      "Iteration: 290, Epoch: 2, Loss: 1.14403, Batch_BER: 0.25800\n",
      "Iteration: 300, Epoch: 2, Loss: 1.14419, Batch_BER: 0.24600\n",
      "Iteration: 310, Epoch: 2, Loss: 1.14505, Batch_BER: 0.24600\n",
      "Iteration: 320, Epoch: 2, Loss: 1.14638, Batch_BER: 0.26600\n",
      "Iteration: 330, Epoch: 2, Loss: 1.14583, Batch_BER: 0.27200\n",
      "Iteration: 340, Epoch: 2, Loss: 1.14751, Batch_BER: 0.25800\n",
      "Iteration: 350, Epoch: 2, Loss: 1.14847, Batch_BER: 0.25200\n",
      "Iteration: 360, Epoch: 2, Loss: 1.14842, Batch_BER: 0.27800\n",
      "Iteration: 370, Epoch: 2, Loss: 1.14763, Batch_BER: 0.24600\n",
      "Iteration: 380, Epoch: 2, Loss: 1.14679, Batch_BER: 0.25800\n",
      "Iteration: 390, Epoch: 2, Loss: 1.14730, Batch_BER: 0.24800\n",
      "Iteration: 400, Epoch: 2, Loss: 1.14595, Batch_BER: 0.23200\n",
      "Interim result for Epoch: 2, Loss: 1.14595, Batch_BER: 0.23200\n",
      "Training Bob in Epoch 3/5\n",
      "Iteration: 10, Epoch: 3, Loss: 1.16644, Batch_BER: 0.28600\n",
      "Iteration: 20, Epoch: 3, Loss: 1.14904, Batch_BER: 0.28000\n",
      "Iteration: 30, Epoch: 3, Loss: 1.14330, Batch_BER: 0.27400\n",
      "Iteration: 40, Epoch: 3, Loss: 1.15322, Batch_BER: 0.28000\n",
      "Iteration: 50, Epoch: 3, Loss: 1.14230, Batch_BER: 0.25200\n",
      "Iteration: 60, Epoch: 3, Loss: 1.14168, Batch_BER: 0.29800\n",
      "Iteration: 70, Epoch: 3, Loss: 1.14046, Batch_BER: 0.23800\n",
      "Iteration: 80, Epoch: 3, Loss: 1.13730, Batch_BER: 0.25200\n",
      "Iteration: 90, Epoch: 3, Loss: 1.13722, Batch_BER: 0.25600\n",
      "Iteration: 100, Epoch: 3, Loss: 1.13182, Batch_BER: 0.27200\n",
      "Iteration: 110, Epoch: 3, Loss: 1.13273, Batch_BER: 0.22000\n",
      "Iteration: 120, Epoch: 3, Loss: 1.12953, Batch_BER: 0.27000\n",
      "Iteration: 130, Epoch: 3, Loss: 1.12930, Batch_BER: 0.24600\n",
      "Iteration: 140, Epoch: 3, Loss: 1.13118, Batch_BER: 0.26400\n",
      "Iteration: 150, Epoch: 3, Loss: 1.13133, Batch_BER: 0.23600\n",
      "Iteration: 160, Epoch: 3, Loss: 1.13145, Batch_BER: 0.27200\n",
      "Iteration: 170, Epoch: 3, Loss: 1.13071, Batch_BER: 0.25600\n",
      "Iteration: 180, Epoch: 3, Loss: 1.12981, Batch_BER: 0.23800\n",
      "Iteration: 190, Epoch: 3, Loss: 1.12762, Batch_BER: 0.25800\n",
      "Iteration: 200, Epoch: 3, Loss: 1.12538, Batch_BER: 0.24000\n",
      "Iteration: 210, Epoch: 3, Loss: 1.12469, Batch_BER: 0.25200\n",
      "Iteration: 220, Epoch: 3, Loss: 1.12234, Batch_BER: 0.25200\n",
      "Iteration: 230, Epoch: 3, Loss: 1.12307, Batch_BER: 0.26000\n",
      "Iteration: 240, Epoch: 3, Loss: 1.12244, Batch_BER: 0.22800\n",
      "Iteration: 250, Epoch: 3, Loss: 1.12494, Batch_BER: 0.28600\n",
      "Iteration: 260, Epoch: 3, Loss: 1.12403, Batch_BER: 0.25400\n",
      "Iteration: 270, Epoch: 3, Loss: 1.12354, Batch_BER: 0.24800\n",
      "Iteration: 280, Epoch: 3, Loss: 1.12329, Batch_BER: 0.29000\n",
      "Iteration: 290, Epoch: 3, Loss: 1.12414, Batch_BER: 0.24000\n",
      "Iteration: 300, Epoch: 3, Loss: 1.12402, Batch_BER: 0.24800\n",
      "Iteration: 310, Epoch: 3, Loss: 1.12445, Batch_BER: 0.24400\n",
      "Iteration: 320, Epoch: 3, Loss: 1.12494, Batch_BER: 0.25800\n",
      "Iteration: 330, Epoch: 3, Loss: 1.12408, Batch_BER: 0.23000\n",
      "Iteration: 340, Epoch: 3, Loss: 1.12388, Batch_BER: 0.25200\n",
      "Iteration: 350, Epoch: 3, Loss: 1.12240, Batch_BER: 0.23600\n",
      "Iteration: 360, Epoch: 3, Loss: 1.12315, Batch_BER: 0.27000\n",
      "Iteration: 370, Epoch: 3, Loss: 1.12267, Batch_BER: 0.26600\n",
      "Iteration: 380, Epoch: 3, Loss: 1.12219, Batch_BER: 0.25400\n",
      "Iteration: 390, Epoch: 3, Loss: 1.12280, Batch_BER: 0.26400\n",
      "Iteration: 400, Epoch: 3, Loss: 1.12206, Batch_BER: 0.24800\n",
      "Interim result for Epoch: 3, Loss: 1.12206, Batch_BER: 0.24800\n",
      "Training Bob in Epoch 4/5\n",
      "Iteration: 10, Epoch: 4, Loss: 1.10936, Batch_BER: 0.23200\n",
      "Iteration: 20, Epoch: 4, Loss: 1.12652, Batch_BER: 0.25600\n",
      "Iteration: 30, Epoch: 4, Loss: 1.13850, Batch_BER: 0.26600\n",
      "Iteration: 40, Epoch: 4, Loss: 1.13872, Batch_BER: 0.27000\n",
      "Iteration: 50, Epoch: 4, Loss: 1.13887, Batch_BER: 0.26600\n",
      "Iteration: 60, Epoch: 4, Loss: 1.13388, Batch_BER: 0.26400\n",
      "Iteration: 70, Epoch: 4, Loss: 1.13076, Batch_BER: 0.25400\n",
      "Iteration: 80, Epoch: 4, Loss: 1.13068, Batch_BER: 0.25200\n",
      "Iteration: 90, Epoch: 4, Loss: 1.13085, Batch_BER: 0.22000\n",
      "Iteration: 100, Epoch: 4, Loss: 1.13085, Batch_BER: 0.23800\n",
      "Iteration: 110, Epoch: 4, Loss: 1.13267, Batch_BER: 0.23600\n",
      "Iteration: 120, Epoch: 4, Loss: 1.12738, Batch_BER: 0.24200\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration: 130, Epoch: 4, Loss: 1.12973, Batch_BER: 0.27600\n",
      "Iteration: 140, Epoch: 4, Loss: 1.12993, Batch_BER: 0.24800\n",
      "Iteration: 150, Epoch: 4, Loss: 1.13071, Batch_BER: 0.26600\n",
      "Iteration: 160, Epoch: 4, Loss: 1.12968, Batch_BER: 0.29200\n",
      "Iteration: 170, Epoch: 4, Loss: 1.12850, Batch_BER: 0.24400\n",
      "Iteration: 180, Epoch: 4, Loss: 1.12649, Batch_BER: 0.26200\n",
      "Iteration: 190, Epoch: 4, Loss: 1.12542, Batch_BER: 0.25400\n",
      "Iteration: 200, Epoch: 4, Loss: 1.12466, Batch_BER: 0.24200\n",
      "Iteration: 210, Epoch: 4, Loss: 1.12782, Batch_BER: 0.24400\n",
      "Iteration: 220, Epoch: 4, Loss: 1.12695, Batch_BER: 0.21600\n",
      "Iteration: 230, Epoch: 4, Loss: 1.12563, Batch_BER: 0.25400\n",
      "Iteration: 240, Epoch: 4, Loss: 1.12611, Batch_BER: 0.24800\n",
      "Iteration: 250, Epoch: 4, Loss: 1.12603, Batch_BER: 0.22800\n",
      "Iteration: 260, Epoch: 4, Loss: 1.12426, Batch_BER: 0.24800\n",
      "Iteration: 270, Epoch: 4, Loss: 1.12552, Batch_BER: 0.24800\n",
      "Iteration: 280, Epoch: 4, Loss: 1.12667, Batch_BER: 0.25800\n",
      "Iteration: 290, Epoch: 4, Loss: 1.12524, Batch_BER: 0.26000\n",
      "Iteration: 300, Epoch: 4, Loss: 1.12413, Batch_BER: 0.29600\n",
      "Iteration: 310, Epoch: 4, Loss: 1.12518, Batch_BER: 0.23800\n",
      "Iteration: 320, Epoch: 4, Loss: 1.12489, Batch_BER: 0.25400\n",
      "Iteration: 330, Epoch: 4, Loss: 1.12410, Batch_BER: 0.26400\n",
      "Iteration: 340, Epoch: 4, Loss: 1.12450, Batch_BER: 0.24000\n",
      "Iteration: 350, Epoch: 4, Loss: 1.12541, Batch_BER: 0.29600\n",
      "Iteration: 360, Epoch: 4, Loss: 1.12598, Batch_BER: 0.26800\n",
      "Iteration: 370, Epoch: 4, Loss: 1.12380, Batch_BER: 0.23000\n",
      "Iteration: 380, Epoch: 4, Loss: 1.12343, Batch_BER: 0.26000\n",
      "Iteration: 390, Epoch: 4, Loss: 1.12295, Batch_BER: 0.24600\n",
      "Iteration: 400, Epoch: 4, Loss: 1.12179, Batch_BER: 0.24800\n",
      "Interim result for Epoch: 4, Loss: 1.12179, Batch_BER: 0.24800\n",
      "Training Bob in Epoch 5/5\n",
      "Iteration: 10, Epoch: 5, Loss: 1.12406, Batch_BER: 0.22800\n",
      "Iteration: 20, Epoch: 5, Loss: 1.09145, Batch_BER: 0.25200\n",
      "Iteration: 30, Epoch: 5, Loss: 1.08990, Batch_BER: 0.22800\n",
      "Iteration: 40, Epoch: 5, Loss: 1.08844, Batch_BER: 0.24600\n",
      "Iteration: 50, Epoch: 5, Loss: 1.09978, Batch_BER: 0.29400\n",
      "Iteration: 60, Epoch: 5, Loss: 1.10904, Batch_BER: 0.27800\n",
      "Iteration: 70, Epoch: 5, Loss: 1.10657, Batch_BER: 0.23000\n",
      "Iteration: 80, Epoch: 5, Loss: 1.10932, Batch_BER: 0.24800\n",
      "Iteration: 90, Epoch: 5, Loss: 1.11353, Batch_BER: 0.27800\n",
      "Iteration: 100, Epoch: 5, Loss: 1.11218, Batch_BER: 0.26200\n",
      "Iteration: 110, Epoch: 5, Loss: 1.11109, Batch_BER: 0.28800\n",
      "Iteration: 120, Epoch: 5, Loss: 1.11358, Batch_BER: 0.26200\n",
      "Iteration: 130, Epoch: 5, Loss: 1.11135, Batch_BER: 0.23000\n",
      "Iteration: 140, Epoch: 5, Loss: 1.11286, Batch_BER: 0.26000\n",
      "Iteration: 150, Epoch: 5, Loss: 1.11367, Batch_BER: 0.24400\n",
      "Iteration: 160, Epoch: 5, Loss: 1.11314, Batch_BER: 0.20800\n",
      "Iteration: 170, Epoch: 5, Loss: 1.11403, Batch_BER: 0.26400\n",
      "Iteration: 180, Epoch: 5, Loss: 1.11287, Batch_BER: 0.28000\n",
      "Iteration: 190, Epoch: 5, Loss: 1.11171, Batch_BER: 0.26200\n",
      "Iteration: 200, Epoch: 5, Loss: 1.11092, Batch_BER: 0.26000\n",
      "Iteration: 210, Epoch: 5, Loss: 1.11203, Batch_BER: 0.25000\n",
      "Iteration: 220, Epoch: 5, Loss: 1.11215, Batch_BER: 0.27200\n",
      "Iteration: 230, Epoch: 5, Loss: 1.11322, Batch_BER: 0.22600\n",
      "Iteration: 240, Epoch: 5, Loss: 1.11241, Batch_BER: 0.21800\n",
      "Iteration: 250, Epoch: 5, Loss: 1.11434, Batch_BER: 0.26000\n",
      "Iteration: 260, Epoch: 5, Loss: 1.11489, Batch_BER: 0.29200\n",
      "Iteration: 270, Epoch: 5, Loss: 1.11504, Batch_BER: 0.24400\n",
      "Iteration: 280, Epoch: 5, Loss: 1.11520, Batch_BER: 0.27200\n",
      "Iteration: 290, Epoch: 5, Loss: 1.11558, Batch_BER: 0.23000\n",
      "Iteration: 300, Epoch: 5, Loss: 1.11557, Batch_BER: 0.25400\n",
      "Iteration: 310, Epoch: 5, Loss: 1.11493, Batch_BER: 0.24600\n",
      "Iteration: 320, Epoch: 5, Loss: 1.11354, Batch_BER: 0.26400\n",
      "Iteration: 330, Epoch: 5, Loss: 1.11332, Batch_BER: 0.25200\n",
      "Iteration: 340, Epoch: 5, Loss: 1.11253, Batch_BER: 0.26400\n",
      "Iteration: 350, Epoch: 5, Loss: 1.11179, Batch_BER: 0.26000\n",
      "Iteration: 360, Epoch: 5, Loss: 1.11044, Batch_BER: 0.26800\n",
      "Iteration: 370, Epoch: 5, Loss: 1.11051, Batch_BER: 0.24800\n",
      "Iteration: 380, Epoch: 5, Loss: 1.10916, Batch_BER: 0.24200\n",
      "Iteration: 390, Epoch: 5, Loss: 1.10744, Batch_BER: 0.21800\n",
      "Iteration: 400, Epoch: 5, Loss: 1.10849, Batch_BER: 0.26000\n",
      "Interim result for Epoch: 5, Loss: 1.10849, Batch_BER: 0.26000\n"
     ]
    }
   ],
   "source": [
    "score_fn = NN_function(**critic_params)\n",
    "train_mi(score_fn, n_epochs=1, n_steps=500, batch_size=64)\n",
    "train_encoder(score_fn, n_epochs=5, n_steps=400, batch_size=64, learning_rate=0.005)\n",
    "test_encoding(M, 1)\n",
    "train_decoder(n_epochs=5, n_steps=400, batch_size=500, learning_rate=0.005, plot_encoding=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Progress: 6 of 30 parts\n",
      "Progress: 12 of 30 parts\n",
      "Progress: 18 of 30 parts\n",
      "Progress: 24 of 30 parts\n",
      "Progress: 30 of 30 parts\n"
     ]
    }
   ],
   "source": [
    "bber_data = Test_AE()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 576x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Approximate 16 QAM Error\n",
    "def SIXT_QAM_sim(ebno):\n",
    "    return (3.0/2)*special.erfc(np.sqrt((4.0/10)*10.**(ebno/10)))\n",
    "\n",
    "def MQAM_rayleigh_approx(M, ebnodb):\n",
    "    ebno = 10.**(ebnodb/10)\n",
    "    esno = 4*ebno\n",
    "    #Goldsmith, p.185, 6.3.2, Eqn 6.61, alphaM=4, betaM=3/(M-1)\n",
    "    a=3.25 #adjusted mean number of neighbors\n",
    "    b=3/(M-1)\n",
    "    e=b*esno\n",
    "    return (a/2)*(1-np.sqrt(0.5*e / (1+0.5*e) ) ), a/(2*b*esno)\n",
    "\n",
    "ebnodbs = np.linspace(0,15,16)\n",
    "fig = plt.figure(figsize=(8, 5))\n",
    "plt.semilogy(bber_data[0], bber_data[1], 'o-')\n",
    "\n",
    "a, b = MQAM_rayleigh_approx(16,ebnodbs)\n",
    "plt.plot(ebnodbs, a);\n",
    "#plt.semilogy(ebnodbs, MQAM_rayleigh_approx(M, ebnodbs), '^-');\n",
    "plt.gca().set_ylim(1e-2, 1)\n",
    "plt.gca().set_xlim(0, 15)\n",
    "plt.ylabel(\"Batch Symbol Error Rate\", fontsize=14, rotation=90)\n",
    "plt.xlabel(\"EbN0 [dB]\", fontsize=18)\n",
    "plt.legend(['AE with MINE', '16QAM'],\n",
    "           prop={'size': 14}, loc='upper right');\n",
    "plt.grid(True, which=\"both\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
